﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.IO;

namespace Vbyte.SharpOrm.ClassGen
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                if (args.Length > 1)
                {
                    GeneratorClass(args[1], args[0], Console.Out);
                }
                else if (args.Length > 0)
                {
                    GeneratorClass("DefaultDb", args[0], Console.Out);
                }
                else
                {
                    Console.WriteLine("[AppName] 数据库表名 [连接字符串键名]");
                }
            }
            catch (Exception exp)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine(exp.StackTrace);
                Console.ResetColor(); 
            }
            //Console.Read();
        }

        private static Dictionary<string, Type> GetTypeDictionary(SqlConnection conn, string tablename)
        {
            string sqlQuery = "select top 1 * from " + tablename + "";
            Dictionary<string, Type> typeDict = new Dictionary<string, Type>();

            DataTable dTab = new DataTable();
            SqlDataAdapter adp = new SqlDataAdapter(sqlQuery, conn);
            adp.Fill(dTab);
            if (dTab != null && dTab.Rows.Count > 0)
            {
                foreach (DataColumn col in dTab.Columns)
                {
                    //Console.WriteLine(col.ColumnName);
                    typeDict.Add(col.ColumnName.Trim().ToLower(), col.DataType);
                }
            }

            //Console.WriteLine(typeDict.Count);
            //Console.WriteLine("-----------------------------------");

            adp.Dispose();
            return typeDict;
        }

        /// <summary>
        /// 首字母大写
        /// </summary>
        public static string CapitalUpperCase(string str)
        {
            if (str == null) return string.Empty;
            string[] objWords = str.Split(' ');
            for (int i = 0; i < objWords.Length; i++)
            {
                if (objWords[i] != string.Empty)
                {
                    //objWords[i] = objWords[i].ToLower();
                    objWords[i] = objWords[i].Substring(0, 1).ToUpper() + objWords[i].Substring(1);
                }
            }
            return String.Join(" ", objWords);
        }

        static string GetSimpleType(Type type, string sqlType)
        {
            Dictionary<Type, string> tDic = new Dictionary<Type, string>();
            tDic.Add(typeof(long), "long");
            tDic.Add(typeof(string), "string");
            tDic.Add(typeof(short), "short");
            tDic.Add(typeof(DateTime), "DateTime");
            tDic.Add(typeof(decimal), "decimal");
            tDic.Add(typeof(bool), "bool");
            tDic.Add(typeof(int), "int");
            tDic.Add(typeof(byte), "byte");

            if (type != null && tDic.ContainsKey(type))
            {
                return tDic[type];
            }
            else
            {
                if (type != null)
                {
                    return type.ToString();
                }
                else
                {
                    Dictionary<string, string> sql2cs = new Dictionary<string, string>();
                    string[] mapConfig = @"varchar,string,varchar2,string,nvarchar,string,nvarchar2,string,char,string,nchar,string,
                        text,string,longtext,string,ntext,string,string,string,date,DateTime,datetime,DateTime,
                        smalldatetime,DateTime,smallint,short,int,int,number,int,bigint,long,tinyint,byte,
                        float,decimal,numeric,decimal,decimal,decimal,money,decimal,smallmoney,decimal,
                        real,decimal,bit,bool,binary,byte[],varbinary,byte[],image,byte[],raw,byte[],long,byte[],
                        long raw,byte[],blob,byte[],bfile,byte[],uniqueidentifier,Guid,integer,int,double,decimal,
                        enum,Enum,timestamp,DateTime".Split(',');
                    for (int i = 0; i < mapConfig.Length; i=i+2)
                    {
                        sql2cs.Add(mapConfig[i].Trim(), mapConfig[i + 1].Trim());
                    }
                    #region 参考转换
                    //[DbToCS]
                    //varchar=string
                    //varchar2=string
                    //nvarchar=string
                    //nvarchar2=string
                    //char=string
                    //nchar=string
                    //text=string
                    //longtext=string
                    //ntext=string
                    //string=string
                    //date=DateTime
                    //datetime=DateTime
                    //smalldatetime=DateTime
                    //smallint=int
                    //int=int
                    //number=int
                    //bigint=long
                    //tinyint=int
                    //float=decimal
                    //numeric=decimal
                    //decimal=decimal
                    //money=decimal
                    //smallmoney=decimal
                    //real=decimal
                    //bit=bool
                    //binary=byte[]
                    //varbinary=byte[]
                    //image=byte[]
                    //raw=byte[]
                    //long=byte[]
                    //long raw=byte[]
                    //blob=byte[]
                    //bfile=byte[]
                    //uniqueidentifier=Guid
                    //integer=int
                    //double=decimal
                    //enum=Enum
                    //timestamp=DateTime

                    //[ToSQLProc]
                    //varchar=VarChar
                    //string=VarChar
                    //nvarchar=NVarChar
                    //char=Char
                    //nchar=NChar
                    //text=Text
                    //ntext=NText
                    //datetime=DateTime
                    //smalldatetime=SmallDateTime
                    //smallint=SmallInt
                    //tinyint=TinyInt
                    //int=Int
                    //bigint=BigInt
                    //float=Float
                    //real=Real
                    //numeric=Decimal
                    //decimal=Decimal
                    //money=Money
                    //smallmoney=SmallMoney
                    //bool=Bit
                    //bit=Bit
                    //binary=Binary
                    //varbinary=VarBinary
                    //image=Image
                    //uniqueidentifier=UniqueIdentifier
                    //timestamp=Timestamp

                    //[ToOraProc]
                    //char=Char
                    //varchar2=VarChar
                    //string=VarChar
                    //nvarchar2=NVarChar
                    //nchar=NChar
                    //long=LongVarChar
                    //number=Number
                    //int=Number
                    //date=DateTime
                    //raw=Raw
                    //long raw=LongRaw
                    //blob=Blob
                    //bit=Clob
                    //clob=Clob
                    //nclob=NClob
                    //bfile=BFile

                    //[ToMySQLProc]
                    //binary=Binary
                    //bool=Bit
                    //bit=Bit
                    //blob=Blob
                    //double=Double
                    //Date=DateTime
                    //datetime=DateTime
                    //numeric=Decimal
                    //decimal=Decimal
                    //float=Float
                    //enum=Enum
                    //geometry=Geometry
                    //longBlob=LongBlob
                    //varchar=VarChar
                    //string=String
                    //char=Char
                    //text=Text
                    //longtext=LongText
                    //time=Time
                    //SmallInt=Int32
                    //TinyInt=Int32
                    //timestamp=Timestamp
                    //tinyText=TinyText
                    //tinyBlob=TinyBlob
                    //int=Int32
                    //varbinary=VarBinary
                    //varstring=VarString
                    //year=Year
                    //varchar=VarChar

                    //[ToOleDbProc]
                    //varchar=VarChar
                    //string=VarChar
                    //nvarchar=LongVarChar
                    //char=Char
                    //nchar=NChar
                    //text=LongVarChar
                    //ntext=LongVarChar
                    //datetime=Date
                    //smalldatetime=Date
                    //smallint=SmallInt
                    //tinyint=TinyInt
                    //int=Integer
                    //bigint=BigInt
                    //money=Decimal
                    //smallmoney=Decimal
                    //float=Decimal
                    //numeric=Decimal
                    //decimal=Decimal
                    //bool=Boolean
                    //bit=Bit
                    //binary=Binary

                    //[IsAddMark]
                    //nvarchar=true
                    //nchar=true
                    //ntext=true
                    //varchar=true
                    //varchar2=true
                    //nvarchar2=true
                    //char=true
                    //clob=true
                    //string=true
                    //text=true
                    //date=true
                    //datetime=true
                    //smalldatetime=true
                    //uniqueidentifier=true

                    //[isValueType]
                    //int=true
                    //Int32=true
                    //Int16=true
                    //Int64=true
                    //DateTime=true
                    //decimal=true
                    //Decimal=true 
	                #endregion
                    if (sql2cs.ContainsKey(sqlType)) return sql2cs[sqlType];
                    return sqlType;
                }
            }
        }

        /// <summary>
        /// 输出数据表实体类
        /// </summary>
        /// <param name="connKey">连接字符串键名</param>
        /// <param name="tablename">表名</param>
        /// <param name="writer">文本输出器</param>
        static void GeneratorClass(string connKey, string tablename, TextWriter writer)
        {
            string connStr = ConfigurationManager.ConnectionStrings[connKey].ConnectionString;
            SqlConnection conn = new SqlConnection(connStr);
            conn.Open();

            SqlCommand cmd = new SqlCommand("select @@Version", conn);
            bool isSql2000 = cmd.ExecuteScalar().ToString().Trim().StartsWith("Microsoft SQL Server 2000");
            cmd.Dispose();

            string sqlQuery = "";

            /*

string sqlQuery = (isSql2000) ? @"select a.name 字段名,isnull(g.[value],'') AS 字段说明 FROM syscolumns as a 
inner join sysobjects d on a.id=d.id and d.xtype='U' and d.name<>'dtproperties' 
left join sysproperties g on a.id=g.id and a.colid=g.smallid  
where d.name = '" + tablename + "' order by a.id,a.colorder" :
                @"select a.name 字段名,isnull(g.[value],'') AS 字段说明 FROM syscolumns as a
 inner join sysobjects d on a.id=d.id and d.xtype='U' and d.name<>'dtproperties'
 left join sys.extended_properties g on a.id=g.major_id AND a.colid = g.minor_id 
where d.name = '" + tablename + "' order by a.id,a.colorder";

             */

            #region 数据库词典
            string tableFilter = (string.IsNullOrEmpty(tablename) || tablename == "*") ? "" : "where d.name = '" + tablename + "'";
            if (isSql2000)
            {
                sqlQuery += @"
                     SELECT  表名=case when a.colorder=1 then d.name else '' end, 
                        字段序号=a.colorder, 字段名=a.name, 
                        标识=case when COLUMNPROPERTY( a.id,a.name,'IsIdentity')=1 then '√'else '' end, 
                        主键=case when exists(SELECT 1 FROM sysobjects where xtype='PK' and name 
                           in (SELECT name FROM sysindexes WHERE indid 
                            in(SELECT indid FROM sysindexkeys WHERE id = a.id AND colid=a.colid  ))) 
                             then '√' else '' end,
                         类型=b.name, 
                         占用字节数=a.length, 
                         长度=COLUMNPROPERTY(a.id,a.name,'PRECISION'), 
                         小数位数=isnull(COLUMNPROPERTY(a.id,a.name,'Scale'),0), 
                         允许空=case when a.isnullable=1 then '√'else '' end, 
                         默认值=isnull(e.text,''), 
                         字段说明=isnull(g.[value],'')
                         FROM syscolumns a left join systypes b on a.xtype=b.xusertype 
                         inner join sysobjects d on a.id=d.id and d.xtype='U' and  d.name<>'dtproperties' 
                         left join syscomments e on a.cdefault=e.id left join sysproperties g on a.id=g.id and a.colid=g.smallid "
                         + tableFilter + " order by a.id,a.colorder";
            }
            else
            {
                sqlQuery += @"
                     SELECT (case when a.colorder=1 then d.name else '' end) 表名, 
					        a.colorder 字段序号, a.name 字段名,
					         (case when COLUMNPROPERTY( a.id,a.name,'IsIdentity')=1 then '√'else '' end) 标识,
					         (case when (SELECT count(*) FROM sysobjects WHERE (name in (SELECT name FROM sysindexes WHERE (id = a.id)
						           AND (indid in (SELECT indid FROM sysindexkeys WHERE (id = a.id)
						           AND (colid in (SELECT colid FROM syscolumns WHERE (id = a.id)
						           AND (name = a.name)))))))
				           AND (xtype = 'PK'))>0 then '√' else '' end) 主键, 
			         b.name 类型, a.length 占用字节数, COLUMNPROPERTY(a.id,a.name,'PRECISION') as 长度,
			         isnull(COLUMNPROPERTY(a.id,a.name,'Scale'),0) as 小数位数,
			         (case when a.isnullable=1 then '√'else '' end) 允许空,
			         isnull(e.text,'') 默认值, isnull(g.[value],'') AS 字段说明
			          FROM syscolumns a left join systypes b on a.xtype=b.xusertype
			          inner join sysobjects d on a.id=d.id and d.xtype='U' and d.name<>'dtproperties'
			        left join syscomments e on a.cdefault=e.id
			        left join sys.extended_properties g on a.id=g.major_id AND a.colid = g.minor_id "
                    + tableFilter + " order by a.id,a.colorder";
            } 
            #endregion

            DataSet dSet = new DataSet();
            SqlDataAdapter adp = new SqlDataAdapter(sqlQuery, conn);
            adp.Fill(dSet);
            adp.Dispose();

            string currentColName = string.Empty;
            //字段名:表名 字段序号 字段名 标识 主键 类型 占用字节数 长度 小数位数 允许空 默认值 字段说明
            string Indent = new string('\t', 1);
            int iTableCount = 0;
            Dictionary<string, Type> typeDict = new Dictionary<string, Type>(StringComparer.InvariantCultureIgnoreCase);
            bool isSingleTable = !(string.IsNullOrEmpty(tablename) || tablename == "*");
            foreach (DataRow dRow in dSet.Tables[0].Rows)
            {

                if (!isSingleTable) tablename = dRow["表名"].ToString();

                iTableCount++;
                if (!string.IsNullOrEmpty(tablename))
                {
                    if (iTableCount > 1)
                    {
                        writer.WriteLine("}");
                    } 
                    
                    typeDict = GetTypeDictionary(conn, tablename);
                    Console.WriteLine();
                    Console.ForegroundColor = ConsoleColor.DarkGreen;
                    Console.WriteLine("/****生成表[" + tablename + "]的实体：****/");
                    Console.ResetColor();

                    writer.WriteLine("[Serializable]");
                    writer.WriteLine("[BindTable(\"" + connKey + "\", \"" + tablename + "\")]");
                    writer.WriteLine(string.Format("public class {0} : TableEntry", CapitalUpperCase(tablename)));
                    writer.WriteLine("{");

                    if (isSingleTable) tablename = string.Empty;
                }

                currentColName = dRow["字段名"].ToString();
                writer.WriteLine(Indent + "/// <summary>");
                writer.WriteLine(Indent + "/// " + dRow["字段说明"].ToString()
                    .Replace("\r\n", "\r\n"+Indent + "/// "));
                writer.WriteLine(Indent + "/// </summary>");
                if (dRow["主键"].ToString() == "√")
                {
                    writer.WriteLine(Indent + "[PrimaryKey]");
                }
                if (dRow["标识"].ToString() == "√")
                {
                    writer.WriteLine(Indent + "[Identity]");
                }

                //Console.ForegroundColor = ConsoleColor.Red;
                //Console.WriteLine(currentColName);
                //Console.ResetColor();

                string key = currentColName.Trim().ToLower();

                //长度限制
                if (dRow["长度"].ToString().Trim() != string.Empty)
                {
                    if (",varchar,nvarchar,char,nchar,".IndexOf("," + dRow["类型"].ToString().Trim() + ",",
                        StringComparison.InvariantCultureIgnoreCase) != -1)
                    {
                        writer.WriteLine("{0}[MaxLength({1})]", Indent, dRow["长度"]);
                    }
                }

                //不为null限制
                if (dRow["允许空"].ToString().Trim() == string.Empty)
                {
                    writer.WriteLine("{0}[Nullable(false)]", Indent);
                }

                if (typeDict != null && typeDict.Count > 0 && typeDict.ContainsKey(key))
                {
                    writer.WriteLine(string.Format("{4}public {3} {0} {1} get; set; {2}", CapitalUpperCase(currentColName),
                        "{", "}",
                        GetSimpleType(typeDict[key], dRow["类型"].ToString()),
                        Indent));
                }
                else
                {
                    writer.WriteLine(string.Format("{4}public {3} {0} {1} get; set; {2}", CapitalUpperCase(currentColName),
                       "{", "}",
                       GetSimpleType(null, dRow["类型"].ToString()),
                       Indent));
                }
                writer.WriteLine();
            }
            conn.Close();
            conn.Dispose();

            if (dSet.Tables[0] != null && dSet.Tables[0].Rows.Count > 0)
            {
                writer.WriteLine("}");
                Console.ForegroundColor = ConsoleColor.DarkGreen;
                Console.WriteLine("/****生成完成！****/");
                Console.ResetColor();
            }
            else
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Error,没有相关记录！");
                Console.ResetColor();
            }
            dSet.Dispose();

        }

    }
}
